﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Xceed.Silverlight.Data;

namespace Xceed.Silverlight.ListBox
{
  internal class SelectedItemsAsyncResult : IAsyncResult
  {
    internal SelectedItemsAsyncResult( IEnumerable<SelectionRange> selectionRanges, AsyncCallback asyncCallback, object asyncState, DataSourceWrapper dataSource )
    {
      if( selectionRanges == null )
        throw new ArgumentNullException( "selectionRanges" );

      if( dataSource == null )
        throw new ArgumentNullException( "dataSource" );

      this.AsyncState = asyncState;
      this.AsyncCallback = asyncCallback;

      this.SelectedItems = new List<object>();

      this.IsCompleted = false;
      this.CompletedSynchronously = true;

      m_enumerator = dataSource.GetDataRangeEnumerator( this.GetDataRangeEnumerator( selectionRanges ) );
      m_enumerator.Reset();
    }

    internal List<object> SelectedItems
    {
      get;
      private set;
    }

    internal AsyncCallback AsyncCallback
    {
      get;
      private set;
    }

    internal void Launch()
    {
      this.WorkerMethod( null );
    }

    private void WorkerMethod( IAsyncResult asyncResult )
    {
      bool isAsyncCallback = false;

      //If this method was involed as the async callback for a BeginMoveNext
      if( asyncResult != null )
      {
        //If the call was completed synchronously, destack, processing will be done in calling stack frame.
        if( asyncResult.CompletedSynchronously )
          return;

        if( !asyncResult.IsCompleted )
          throw new ListBoxInternalException(); //AsyncCallback was called, but IAsyncResult indicates it has not completed.

        isAsyncCallback = true;
      }

      bool completedSynchronously = true;
      bool moveNextSucceeded = true;
      do
      {
        //If there is already a IAsyncResult, then we do not call another moveNext
        if( !isAsyncCallback )
        {
          asyncResult = m_enumerator.BeginGetNext( new AsyncCallback( WorkerMethod ), null );
        }

        if( asyncResult.IsCompleted )
        {
          GetNextResult result = m_enumerator.EndGetNext( asyncResult );
          moveNextSucceeded = result.Success;

          if( moveNextSucceeded )
          {
            this.SelectedItems.Add( result.Item.LastChild.Data );
          }
        }
        else
        {
          Debug.Assert( !isAsyncCallback );

          completedSynchronously = false;
        }

        //reset the flag to enable another iteration of the loop.
        isAsyncCallback = false;
      }
      while( completedSynchronously && moveNextSucceeded );

      if( !completedSynchronously )
      {
        this.CompletedSynchronously = false;
      }

      if( !moveNextSucceeded )
      {
        this.IsCompleted = true;

        if( this.AsyncCallback != null )
        {
          this.AsyncCallback.Invoke( this );
        }
      }

    }

    private IEnumerable<DataRange> GetDataRangeEnumerator( IEnumerable<SelectionRange> selectionRanges )
    {
      foreach( SelectionRange selectionRange in selectionRanges )
      {
        yield return this.ConvertSelectionRange( selectionRange );
      }
    }

    private DataRange ConvertSelectionRange( SelectionRange selectionRange )
    {
      DataRangeContext dataRangeContext = selectionRange.DataRangeContext;
      bool isSelection = ( selectionRange.SelectionType == SelectionType.Selection );

      if( selectionRange.IsSingleItem )
      {
        Debug.Assert( selectionRange.StartRangeInfos.TargetPath == selectionRange.EndRangeInfos.TargetPath, "Invalid selection range." );

        DataPath singleItem = selectionRange.StartRangeInfos.TargetPath;

        Debug.Assert( singleItem != null, "Invalid single item selection." );

        bool isLowerBoundInclusive = selectionRange.StartRangeInfos.IsInclusive;
        bool isUpperBoundInclusive = selectionRange.EndRangeInfos.IsInclusive;

        return DataRange.From( isSelection, singleItem, isLowerBoundInclusive, singleItem, isUpperBoundInclusive, dataRangeContext );
      }
      else
      {
        IDictionary<string, object> lowerBound;
        IDictionary<string, object> upperBound;
        bool isLowerBoundInclusive;
        bool isUpperBoundInclusive;

        lowerBound = selectionRange.GetLowerBound( out isLowerBoundInclusive );
        upperBound = selectionRange.GetUpperBound( out isUpperBoundInclusive );

        if( !selectionRange.SelectFromStart && !selectionRange.SelectToEnd )
        {
          return DataRange.From( isSelection, lowerBound, isLowerBoundInclusive, upperBound, isUpperBoundInclusive, dataRangeContext );
        }
        else if( !selectionRange.SelectFromStart )
        {
          return DataRange.From( isSelection, lowerBound, isLowerBoundInclusive, dataRangeContext );
        }
        else if( !selectionRange.SelectToEnd )
        {
          return DataRange.To( isSelection, upperBound, isUpperBoundInclusive, dataRangeContext );
        }
        else
        {
          return DataRange.All( isSelection, dataRangeContext );
        }
      }
    }

    #region IAsyncResult Members

    public object AsyncState
    {
      get;
      private set;
    }

    public WaitHandle AsyncWaitHandle
    {
      get
      {
        if( m_waitHandle == null )
        {
          m_waitHandle = new ManualResetEvent( this.IsCompleted );
        }

        return m_waitHandle;
      }
    }

    public bool CompletedSynchronously
    {
      get;
      private set;
    }

    public bool IsCompleted
    {
      get
      {
        return m_isCompleted;
      }
      private set
      {
        if( value != m_isCompleted )
        {
          m_isCompleted = value;

          if( value && ( m_waitHandle != null ) )
          {
            m_waitHandle.Set();
          }
        }
      }
    }

    #endregion

    private IDataRangeEnumerator m_enumerator;
    private ManualResetEvent m_waitHandle;//null
    private bool m_isCompleted;//false
  }
}
